﻿using System.Linq;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Command;
using HP.Cachalote.WPF.Model;
using HP.Cachalote.Services;
using HP.Cachalote.Services.Interfaces;
using HP.Cachalote.Services.Shared;
using HP.Cachalote.WPF.Shared;

namespace HP.Cachalote.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class TodoItemFilterViewModel : MyViewModelBase
    {
        private ITodoItemService _itemService;

        /// <summary>
        /// Initializes a new instance of the TodoItemFilterViewModel class.
        /// </summary>
        public TodoItemFilterViewModel(IDataService dataService)
            : base(dataService)
        {
            _itemService = ServiceFactory.CreateTodoItemService();
            
            LoadToItemCount();
        }

        /// <summary>
        /// Register all necessary messages. It is called by constructor in base class.
        /// </summary>
        protected override void RegisterMessages()
        {
            base.RegisterMessages();

            RegisterTodoItemsChangedNotification();
            RegisterDueDateChangedMessage();
        }

        /// <summary>
        /// The <see cref="AllTodoItemCount" /> property's name.
        /// </summary>
        public const string AllTodoItemCountPropertyName = "AllTodoItemCount";

        private int _allTodoItemCount = 0;

        /// <summary>
        /// Sets and gets the AllTodoItemCount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int AllTodoItemCount
        {
            get
            {
                return _allTodoItemCount;
            }

            set
            {
                if (_allTodoItemCount == value)
                {
                    return;
                }

                RaisePropertyChanging(AllTodoItemCountPropertyName);
                _allTodoItemCount = value;
                RaisePropertyChanged(AllTodoItemCountPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="TodayTodoItemCount" /> property's name.
        /// </summary>
        public const string TodayTodoItemCountPropertyName = "TodayTodoItemCount";

        private int _todayTodoItemCount = 0;

        /// <summary>
        /// Sets and gets the TodayTodoItemCount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int TodayTodoItemCount
        {
            get
            {
                return _todayTodoItemCount;
            }

            set
            {
                if (_todayTodoItemCount == value)
                {
                    return;
                }

                RaisePropertyChanging(TodayTodoItemCountPropertyName);
                _todayTodoItemCount = value;
                RaisePropertyChanged(TodayTodoItemCountPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="TomorrowTodoItemCount" /> property's name.
        /// </summary>
        public const string TomorrowTodoItemCountPropertyName = "TomorrowTodoItemCount";

        private int _tomorrowTodoItemCount = 0;

        /// <summary>
        /// Sets and gets the TomorrowTodoItemCount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int TomorrowTodoItemCount
        {
            get
            {
                return _tomorrowTodoItemCount;
            }

            set
            {
                if (_tomorrowTodoItemCount == value)
                {
                    return;
                }

                RaisePropertyChanging(TomorrowTodoItemCountPropertyName);
                _tomorrowTodoItemCount = value;
                RaisePropertyChanged(TomorrowTodoItemCountPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="NextTodoItemCount" /> property's name.
        /// </summary>
        public const string NextTodoItemCountPropertyName = "NextTodoItemCount";

        private int _nextTodoItemCount = 0;

        /// <summary>
        /// Sets and gets the NextTodoItemCount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int NextTodoItemCount
        {
            get
            {
                return _nextTodoItemCount;
            }

            set
            {
                if (_nextTodoItemCount == value)
                {
                    return;
                }

                RaisePropertyChanging(NextTodoItemCountPropertyName);
                _nextTodoItemCount = value;
                RaisePropertyChanged(NextTodoItemCountPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="OverdueTodoItemCount" /> property's name.
        /// </summary>
        public const string OverdueTodoItemCountPropertyName = "OverdueTodoItemCount";

        private int _overdueTodoItemCount = 0;

        /// <summary>
        /// Sets and gets the OverdueTodoItemCount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int OverdueTodoItemCount
        {
            get
            {
                return _overdueTodoItemCount;
            }

            set
            {
                if (_overdueTodoItemCount == value)
                {
                    return;
                }

                RaisePropertyChanging(OverdueTodoItemCountPropertyName);
                _overdueTodoItemCount = value;
                RaisePropertyChanged(OverdueTodoItemCountPropertyName);
            }
        }

        private RelayCommand<string> _itemClickCommand;

        /// <summary>
        /// Gets the GotFocusCommand.
        /// </summary>
        public RelayCommand<string> ItemClickCommand
        {
            get
            {
                return _itemClickCommand
                    ?? (_itemClickCommand = new RelayCommand<string>(
                                          p =>
                                          {
                                              if (!string.IsNullOrEmpty(p))
                                              {
                                                  // If selection has been changed.
                                                  TodoItemFilterType selectedFilterType = TodoItemFilterType.All;

                                                  switch (p.ToLower())
                                                  {
                                                      case "all":
                                                          selectedFilterType = TodoItemFilterType.All;
                                                          break;
                                                      case "today":
                                                          selectedFilterType = TodoItemFilterType.Today;
                                                          break;
                                                      case "tomorrow":
                                                          selectedFilterType = TodoItemFilterType.Tomorrow;
                                                          break;
                                                      case "next":
                                                          selectedFilterType = TodoItemFilterType.Next;
                                                          break;
                                                      case "overdue":
                                                          selectedFilterType = TodoItemFilterType.Overdue;
                                                          break;
                                                  }

                                                  // Send message to FinishedTodoItemTreeViewModel & UnfinishedTodoItemTreeViewModel 
                                                  // to show the TodoItems based on selected TodoLabel or TodoItemFilter.
                                                  Messenger.Default.Send<object>(selectedFilterType,
                                                      TodoLabelTreeViewModel.SelectToShowRelatedTodoItemsMessageToken);
                                              }
                                          }));
            }
        }

        /// <summary>
        /// Load TodoItem count for all filters.
        /// </summary>
        private void LoadToItemCount()
        {
            var allItems = _itemService.Filter(item => !item.IsDone);

            AllTodoItemCount = allItems.Count;
            TodayTodoItemCount = allItems.Where(item => GeneralUtilities.GetFilterType(item.DueDate) == TodoItemFilterType.Today).Count();
            TomorrowTodoItemCount = allItems.Where(item => GeneralUtilities.GetFilterType(item.DueDate) == TodoItemFilterType.Tomorrow).Count();
            NextTodoItemCount = allItems.Where(item => GeneralUtilities.GetFilterType(item.DueDate) == TodoItemFilterType.Next).Count();
            OverdueTodoItemCount = allItems.Where(item => GeneralUtilities.GetFilterType(item.DueDate) == TodoItemFilterType.Overdue).Count();
        }

        /// <summary>
        /// Register to be observer of TodoItems change notification receiver.
        /// </summary>
        private void RegisterTodoItemsChangedNotification()
        {
            Messenger.Default.Register<UnfinishedTodoItemTreeViewModel>(this, UnfinishedTodoItemTreeViewModel.RelatedTodoItemsChangedMessageToken, itemTreeViewModel =>
            {
                if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Done)
                {
                    DecreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
                    AllTodoItemCount -= 1;
                }
                else if (itemTreeViewModel.ActionCategory == UnfinishedTodoItemTreeViewModel.TodoItemActionCategory.Undone)
                {

                    IncreaseRelatedFilterTodoItemCount(itemTreeViewModel.FilterType);
                    AllTodoItemCount += 1;
                }
            });
        }

        /// <summary>
        /// Get the DueDateChangedMessage from DueTimeDisplayViewModel to change the DueDateStatus once it is updated.
        /// </summary>
        private void RegisterDueDateChangedMessage()
        {
            Messenger.Default.Register<DueTimeDisplayViewModel>(this, DueTimeDisplayViewModel.DueDateChangedMessageToken, dueTimeDisplayViewModel =>
            {
                var preChangedFilterType = GeneralUtilities.GetFilterType(dueTimeDisplayViewModel.PreChangedDueDate);
                var postChangedfilterType = GeneralUtilities.GetFilterType(dueTimeDisplayViewModel.DueDate);

                IncreaseRelatedFilterTodoItemCount(postChangedfilterType);
                DecreaseRelatedFilterTodoItemCount(preChangedFilterType);
            });
        }

        /// <summary>
        /// Increase one to related Filter's TodoItem count based on provided FilterType.
        /// </summary>
        /// <param name="filterType">The provided FilterType.</param>
        private void IncreaseRelatedFilterTodoItemCount(TodoItemFilterType filterType)
        {
            switch (filterType)
            {
                case TodoItemFilterType.Today:
                    TodayTodoItemCount += 1;
                    break;
                case TodoItemFilterType.Tomorrow:
                    TomorrowTodoItemCount += 1;
                    break;
                case TodoItemFilterType.Next:
                    NextTodoItemCount += 1;
                    break;
                case TodoItemFilterType.Overdue:
                    OverdueTodoItemCount += 1;
                    break;
            }
        }

        /// <summary>
        /// Decrease one to related Filter's TodoItem count based on provided FilterType.
        /// </summary>
        /// <param name="filterType">The provided FilterType.</param>
        private void DecreaseRelatedFilterTodoItemCount(TodoItemFilterType filterType)
        {
            switch (filterType)
            {
                case TodoItemFilterType.Today:
                    TodayTodoItemCount -= 1;
                    break;
                case TodoItemFilterType.Tomorrow:
                    TomorrowTodoItemCount -= 1;
                    break;
                case TodoItemFilterType.Next:
                    NextTodoItemCount -= 1;
                    break;
                case TodoItemFilterType.Overdue:
                    OverdueTodoItemCount -= 1;
                    break;
            }
        }
    }
}